﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.ComponentModel;
using System.IO;
using System;
using Volo.Abp.DependencyInjection;
using DotLiquid;
using System.Linq;
using System.Collections.Generic;

namespace CodeSmithWpf;

public class CodeGenerateService : ITransientDependency
{
    public ILogger<CodeGenerateService> Logger { get; set; }

    public CodeGenerateService()
    {
        Logger = NullLogger<CodeGenerateService>.Instance;
    }
    public void Generate(TypeDTO typeDTO)
    {
        this.GenerateEntityDto(typeDTO);
        this.GenerateApplicationInterface(typeDTO);
        this.GenerateCreateOrUpdateDto(typeDTO);
        this.GenerateQueryDto(typeDTO);

        this.GenerateApplicationService(typeDTO);

        this.GenerateController(typeDTO);

        this.GenerateApi(typeDTO);
        this.GenerateIndexPage(typeDTO);
        this.GenerateFormComponent(typeDTO);
        this.GenerateDetailComponent(typeDTO);
    }

    #region TypeScript
    private void GenerateApi(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/vue/api.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.ClassType.Name,
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetTypeScriptIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            EntityClassName = typeDTO.ClassType.Name,
            ClassDescription = typeDTO.ClassDescription,
            ApplicationServiceClassName = typeDTO.GetApplicationServiceClassName(),
            ProjectName = typeDTO.ProjectName,
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetTypeScriptName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetTypeScriptTypeName(),
                
            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "admin", "api", typeDTO.ModuleName, typeDTO.GetTypeScriptName()+ ".js");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }

    private void GenerateIndexPage(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/vue/index.txt").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.ClassType.Name,
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetTypeScriptIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            EntityClassName = typeDTO.ClassType.Name,
            ClassDescription = typeDTO.ClassDescription,
            ApplicationServiceClassName = typeDTO.GetApplicationServiceClassName(),
            ProjectName = typeDTO.ProjectName,
            TypeScriptModuleName =typeDTO.ModuleName,
            TypeScriptName = typeDTO.GetTypeScriptName(),
            TypeScriptIndexPageName = typeDTO.GetTypeScriptIndexPageName(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetTypeScriptName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetTypeScriptTypeName(),
                TypeScriptTypeValue = x.GetTypeScriptTypeValue(),

            })).ToList()
        })) ;
        string path = Path.Combine(typeDTO.GeneratePath, "admin", "src","views",typeDTO.ModuleName, typeDTO.GetTypeScriptFileName(),"Index" + ".vue");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }

    private void GenerateFormComponent(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/vue/form.txt").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.ClassType.Name,
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetTypeScriptIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            EntityClassName = typeDTO.ClassType.Name,
            ClassDescription = typeDTO.ClassDescription,
            ApplicationServiceClassName = typeDTO.GetApplicationServiceClassName(),
            ProjectName = typeDTO.ProjectName,
            TypeScriptModuleName = typeDTO.ModuleName,
            TypeScriptName = typeDTO.GetTypeScriptName(),
            TypeScriptFormComponentName = typeDTO.GetTypeScriptFormComponentName(),
            TypeScriptIdTypeName = typeDTO.GetTypeScriptIdTypeName(),
            TypeScriptIdTypeValue = typeDTO.GetTypeScriptIdTypeValue(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetTypeScriptName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetTypeScriptTypeName(),
                TypeScriptTypeValue = x.GetTypeScriptTypeValue(),

            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "admin", "src", "views", typeDTO.ModuleName, typeDTO.GetTypeScriptFileName(), "components", "form" + ".vue");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }

    private void GenerateDetailComponent(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/vue/detail.txt").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.ClassType.Name,
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetTypeScriptIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            EntityClassName = typeDTO.ClassType.Name,
            ClassDescription = typeDTO.ClassDescription,
            ApplicationServiceClassName = typeDTO.GetApplicationServiceClassName(),
            ProjectName = typeDTO.ProjectName,
            TypeScriptModuleName = StringUtil.GetUnderlineCase(typeDTO.ModuleName),
            TypeScriptName = typeDTO.GetTypeScriptName(),
            TypeScriptFormComponentName = typeDTO.GetTypeScriptFormComponentName(),
            TypeScriptIdTypeName = typeDTO.GetTypeScriptIdTypeName(),
            TypeScriptIdTypeValue = typeDTO.GetTypeScriptIdTypeValue(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetTypeScriptName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetTypeScriptTypeName(),
                TypeScriptTypeValue = x.GetTypeScriptTypeValue(),

            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "admin", "src", "views", StringUtil.GetUnderlineCase(typeDTO.ModuleName), typeDTO.GetTypeScriptFileName(), "components", "detail" + ".vue");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }
    #endregion

    public string GenerateSummary(string projectName,string moduleName,List<TypeDTO> list)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/index.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {

            ModuleName = moduleName,
            ProjectName = projectName,
            Classes = list.Select(typeDTO => Hash.FromAnonymousObject( new
            {
                ClassName = typeDTO.ClassType.Name,
                CodeClassName = typeDTO.CodeClassName,
                CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
                IdTypeName = typeDTO.GetIdTypeName(),
                TypeScriptIdTypeName = typeDTO.GetTypeScriptIdTypeName(),
                DtoClassName = typeDTO.GetDtoClassName(),
                QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
                EntityClassName = typeDTO.ClassType.Name,
                ClassDescription = typeDTO.ClassDescription,
                TableName=typeDTO.GetTableName(),
                ClassPluralPropertyName = typeDTO.GetClassPluralPropertyName(),
                TypeScriptModuleName = StringUtil.GetUnderlineCase(moduleName),
                TypeScriptName=typeDTO.GetTypeScriptName(),
                Properties = typeDTO.Properties
                .Select(x => Hash.FromAnonymousObject(new
                {
                    PropertyName = x.GetPropertyName(),
                    PropertyDescription = x.PropertyDescription,
                    PropertyTypeName = x.GetPropertyTypeName()
                })).ToList()
            }))
            
        }));
        return result;
    }

    #region HttpApi
    private void GenerateController(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/controller.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.GetControllerClassName(),
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            EntityClassName = typeDTO.ClassType.Name,
            ClassDescription = typeDTO.ClassDescription,
            Namespace = typeDTO.GetNamespace("HttpApi"),
            ProjectName = typeDTO.ProjectName,            
            ControllerBaseClassName = typeDTO.ProjectName + "Controller",
            NamespaceApplicationContracts = typeDTO.GetNamespace("Application.Contracts"),
            ApplicationInterfaceClassName = typeDTO.GetApplicationInterfaceClassName()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "src", typeDTO.GetNamespace("HttpApi"), typeDTO.ModuleName, typeDTO.GetControllerClassName() + ".cs");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }
    #endregion

    #region Application
    private void GenerateApplicationService(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/service.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.GetApplicationServiceClassName(),
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            EntityClassName = typeDTO.ClassType.Name,
            ClassDescription = typeDTO.ClassDescription,
            Namespace = typeDTO.GetNamespace("Application"),
            NamespaceDomain = typeDTO.GetNamespace(""),
            NamespaceApplicationContracts = typeDTO.GetNamespace("Application.Contracts"),
            NamespaceDomainSharedEnums = typeDTO.GetNamespace("Enums"),
            ApplicationInterfaceClassName = typeDTO.GetApplicationInterfaceClassName(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetPropertyName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetPropertyTypeName()
            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "src", typeDTO.GetNamespace("Application"), typeDTO.ModuleName, typeDTO.GetApplicationServiceClassName() + ".cs");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }
    #endregion

    #region Application.Contracts

    private void GenerateApplicationInterface(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/iservice.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.GetApplicationInterfaceClassName(),
            CodeClassName = typeDTO.CodeClassName,
            CreateOrUpdateClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            IdTypeName = typeDTO.GetIdTypeName(),
            DtoClassName = typeDTO.GetDtoClassName(),
            QueryDtoClassName = typeDTO.GetQueryDtoClassName(),
            ClassDescription = typeDTO.ClassDescription,
            Namespace = typeDTO.GetNamespace("Application.Contracts"),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetPropertyName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetPropertyTypeName()
            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "src", typeDTO.GetNamespace("Application.Contracts"), typeDTO.ModuleName, typeDTO.CodeClassName, typeDTO.GetApplicationInterfaceClassName() + ".cs");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }
    private void GenerateEntityDto(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/entitydto.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.GetDtoClassName(),
            CodeClassName = typeDTO.CodeClassName,
            ClassDescription = typeDTO.ClassDescription,
            Namespace = typeDTO.GetNamespace("Application.Contracts"),
            NamespaceDomainSharedEnums = typeDTO.GetNamespace("Enums"),
            IdTypeName = typeDTO.GetIdTypeName(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetPropertyName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetPropertyTypeName()
            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "src", typeDTO.GetNamespace("Application.Contracts"), typeDTO.ModuleName, typeDTO.CodeClassName, typeDTO.GetDtoClassName() + ".cs");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }
    private void GenerateCreateOrUpdateDto(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/createorupdatedto.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.GetCreateOrUpdateDtoClassName(),
            CodeClassName = typeDTO.CodeClassName,
            ClassDescription = typeDTO.ClassDescription,
            Namespace = typeDTO.GetNamespace("Application.Contracts"),
            NamespaceDomainSharedEnums = typeDTO.GetNamespace("Enums"),
            IdTypeName = typeDTO.GetIdTypeName(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetPropertyName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetPropertyTypeName()
            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "src", typeDTO.GetNamespace("Application.Contracts"), typeDTO.ModuleName, typeDTO.CodeClassName, typeDTO.GetCreateOrUpdateDtoClassName() + ".cs");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }

    private void GenerateQueryDto(TypeDTO typeDTO)
    {
        var templateApplication = DotLiquid.Template.Parse(new StreamReader("tpl/queryinputdto.html").ReadToEnd());
        var result = templateApplication.Render(Hash.FromAnonymousObject(new
        {
            ClassName = typeDTO.GetQueryDtoClassName(),
            CodeClassName = typeDTO.CodeClassName,
            ClassDescription = typeDTO.ClassDescription,
            Namespace = typeDTO.GetNamespace("Application.Contracts"),
            NamespaceDomainSharedEnums = typeDTO.GetNamespace("Enums"),
            IdTypeName = typeDTO.GetIdTypeName(),
            Properties = typeDTO.Properties
            .Select(x => Hash.FromAnonymousObject(new
            {
                PropertyName = x.GetPropertyName(),
                PropertyDescription = x.PropertyDescription,
                PropertyTypeName = x.GetPropertyTypeName()
            })).ToList()
        }));
        string path = Path.Combine(typeDTO.GeneratePath, "src", typeDTO.GetNamespace("Application.Contracts"), typeDTO.ModuleName, typeDTO.CodeClassName, typeDTO.GetQueryDtoClassName() + ".cs");
        if (!Directory.Exists(Path.GetDirectoryName(path)))
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path));
        }
        System.IO.File.WriteAllText(path, result, System.Text.Encoding.UTF8);
    }
    #endregion
}
